'''
* This is the projet for Brtc LlmOps Platform
* @Author Leon-liao <liaosiliang@alltman.com>
* @Description //TODO 
* @File: 7_study_bind_tools.py
* @Time: 2025/11/3
* @All Rights Reserve By Brtc
'''
import json
import os
from typing import Type, Any

import dotenv
import requests
from langchain_community.tools import GoogleSerperRun
from langchain_community.utilities import GoogleSerperAPIWrapper
from langchain_core.messages import ToolMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.tools import BaseTool
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
dotenv.load_dotenv()

class GaodeWeatherShema(BaseModel):
    city:str = Field(description="需要查询天气的城市,例如:武汉")

class GaodeWeatherTool(BaseTool):
    """根据传入的城市名称运行调用api 获取城市的天气预报信息"""
    name:str = "llmops_gaode_weather_tool"
    description:str = "当你查询天气的时候可以调用这个工具"
    args_schema:Type[BaseModel] = GaodeWeatherShema

    def _run(self, *args:Any, **kwargs:Any) -> Any:
        try:
            gaode_api_key = os.getenv("GAODE_API_KEY")
            gaode_api_url = os.getenv("GAODE_API_URL")
            if not gaode_api_key or not gaode_api_url:
                return f"请配置高德开放API_KEY 和 URL"
            else:
                #1、从参数中获取城市
                city = kwargs.get("city")
                #2、开始请求 高德服务获取 adcode
                session = requests.session()
                #3、行政code 请求
                city_response = session.request(
                    method="GET",
                    url = f"{gaode_api_url}/config/district?key={gaode_api_key}&keywords={city}&subdistrict=0",
                    headers={"Content-Type": "application/json; charset=utf-8"},
                )
                city_response.raise_for_status()
                city_data = city_response.json()
                if city_data.get("info") == "OK":
                    ad_code = city_data["districts"][0]["adcode"]
                    weather_info = session.request(
                        method="GET",
                        url=f"{gaode_api_url}/weather/weatherInfo?key={gaode_api_key}&city={ad_code}&extensions=all"
                    )
                    weather_info.raise_for_status()
                    weather_data = weather_info.json()
                    if weather_data.get("info") == "OK":
                        #返回天气的结果
                        return json.dumps(weather_data)
                    else:
                        return f"最后请求参数出错！！"
                else:
                    return f"请求城市编码出错！"
        except Exception as e:
            return f"开始获取天气的时候就出错了！{str(e)}"

class GoogleSerperSchema(BaseModel):
    query:str = Field(description="执行谷歌搜索的查询语句")


#1、定义工具列表
gaode_weather = GaodeWeatherTool()
google_serper = GoogleSerperRun(
    name = "google_serper",
    description = "一个底层本的谷歌工具",
    args_schema= GoogleSerperSchema,
    api_wrapper=GoogleSerperAPIWrapper(),
)
tool_dict = {
    gaode_weather.name:gaode_weather,
    google_serper.name:google_serper,
}
tools = [tool for tool in tool_dict.values()]
#2、创建prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是由OpenAI研发的聊天机器人, 可以帮助用户回答问题, 必要时候请调用工具帮助用户解答问题！"),
    ("human", "{query}")
])

#3、创建大预言模型并绑定
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
llm_with_tool = llm.bind_tools(tools=tools)

#4、创建链应用
chain = {"query":RunnablePassthrough()} | prompt | llm_with_tool

#5、解析输出
query = ("请问今天的 纳斯达克指数 是多少？")

"""  
resp 是AI 输出的函数调用json
tools 的格式是：
"tool_calls": [
          {
            "id": "call_abc123",
            "type": "function",
            "function": {
              "name": "get_current_weather",
              "arguments": "{\n\"location\": \"Boston, MA\"\n}"
            }
          }
        ]
"""
resp = chain.invoke(query)
tool_calls = resp.tool_calls # 拿到了  tool_calls 信息

#6、判断是工具还是正常输出结果
if len(tool_calls)<=0:#r如果 tool——calls 长度 不等于0 说明有函数调用信息
    print("生成了内容:", resp.content)# 没有 工具调用直接返回AI生成的内容
else:# 处理调用信息
    #7、将 历史消息、人类消息、AI消息组合
    messages = prompt.invoke(query).to_messages()# 人类消息
    messages.append(resp)# AI 消息
    #8、循环遍历所有工具调用信息
    for tool_call in tool_calls: # tool_calls 是一个数组遍历调用所有 工具
        tool = tool_dict.get(tool_call.get("name"))#根据 AI 返回的 消息获取工具名称并提取工具实例
        print("正在执行工具:", tool.name)
        id = tool_call.get("id")#tool message的id
        content = tool.invoke(tool_call.get("args"))# 直接调用工具， 参数是从AI 返回的调用信息获取的
        print("工具输出:", content)
        messages.append(ToolMessage(# 合成工具消息， 让AI 总结
            content=content,
            tool_call_id = id,
        ))
        print("输出内容: ", llm.invoke(messages).content)





